#ifndef __JPS_ME_H__
#define __JPS_ME_H__

#include <cstdint>
#include <vector>
#include <queue>
#include <set>
#include <string>
#include <unordered_map>

#include <nan.h>

using namespace std;

struct Pos
{
    int16_t x_;
    int16_t y_;

    Pos() : x_(-1), y_(-1) {}
    Pos(int16_t x, int16_t y) : x_(x), y_(y) {}
};
struct PosHash
{
    size_t operator()(const Pos &hs) const
    {
        return hs.x_ ^ (hs.y_ << 2);
    }
};
struct PosDirectComp
{
    bool operator()(const Pos &lhs, const Pos &rhs)
    {
        int dx = end.x_ - start.x_;
        int dy = end.y_ - start.y_;
        if (dx)
            dx = dx > 0 ? 1 : -1;
        if (dy)
            dy = dy > 0 ? 1 : -1;
        int l = abs(lhs.x_ - dx) + abs(lhs.y_ - dy);
        int r = abs(rhs.x_ - dx) + abs(rhs.y_ - dy);
        return l < r;
    }
    Pos start;
    Pos end;
};

struct State
{
    int16_t x_;
    int16_t y_;
    int32_t cost_;  //从起点的消耗值
    int32_t weigh_; // cost_ + 到终点预估值
    int8_t directX_;
    int8_t directY_;
    bool operator<(const State &s) const
    {
        return this->weigh_ > s.weigh_;
    }
};

struct JpsMe : public Nan::ObjectWrap
{
    vector<vector<int8_t>> mapInfo_;
    inline int mapLength() { return mapInfo_.size(); }
    inline int mapWidth() { return mapInfo_.size() ? mapInfo_[0].size() : 0; }
    void initMapByLengthAndWidth(int mapLength, int mapWidth);
    void initMapByFile(const string &fileName);
    void setMap(Pos leftBottom, Pos rightTop, int8_t t);
    void setMapAndUpdateJp(Pos leftBottom, Pos rightTop, int8_t t);

    inline bool walkAble(int x, int y);
    inline bool sideCheck(int x, int y);
    inline static int evaluateCost(Pos s, Pos e);
    inline static bool posEqual(Pos a, Pos b);

    vector<vector<vector<int16_t>>> jpInfo_;
    bool forceNeighbor(Pos start, Pos direct);
    bool jp(Pos p, Pos direct);
    void initJpInfo();

    bool findEnd(Pos p, Pos end, Pos direct);
    bool addForceNeighbor(State start, Pos end, Pos direct, priority_queue<State> &openList, unordered_map<Pos, Pos, PosHash> &pres);
    bool addJpToOpenList(State p, Pos end, Pos direct, priority_queue<State> &openList, unordered_map<Pos, Pos, PosHash> &pres);
    bool jumpPoint(State start, Pos end, Pos direct, priority_queue<State> &openList, unordered_map<Pos, Pos, PosHash> &pres);
    void search(Pos start, Pos end, vector<Pos> &result);

    static NAN_MODULE_INIT(Init);
    static NAN_METHOD(New);
    static Nan::Persistent<v8::Function> constructor;

    static NAN_METHOD(MAPLENGTH);
    static NAN_METHOD(MAPWIDTH);
    static NAN_METHOD(INITMAPBYLENGTHANDWIDTH);
    static NAN_METHOD(INITMAPBYFILE);
    static NAN_METHOD(INITJPINFO);
    static NAN_METHOD(SETMAP);
    static NAN_METHOD(SETMAPANDUPDATEJP);
    static NAN_METHOD(SEARCH);
};
#endif